﻿/***************************************************************
* File    : BluetoothConnection.cs
* Author  : Kyle Burgess
*
* Description:
*   Bluetooth Interface Control
*
* History :
***************************************************************/

//Necessary header files
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using InTheHand.Net; 
using InTheHand.Net.Sockets;
using InTheHand.Net.Bluetooth;
using System.Windows.Forms;

namespace RLGDashboard
{
    public class BluetoothConnection
    {
        //Private attributes of class
        private InQueue transmissionQueue;
        private InQueue recordingQueue;
        private Thread listen;
        private BluetoothDeviceInfo[] devices;
        private BluetoothClient bc;
        private BluetoothListener bl;
        private BinaryReader read;
        private BinaryWriter write;
        private BluetoothTransmission transmissionData;
        private bool connected = false;
        private bool recording = false;

        /***************************************************************
        * Public                 BluetoothConnection
        *
        * Bluetooth function to intitialize the Bluetooth settings
        *
        * Inputs
        * None
        * 
        * Outputs
        * None
        * 
        * History:
        ****************************************************************/
        public BluetoothConnection()
        {
            transmissionData = new BluetoothTransmission();
            connected = false;
        }

        /***************************************************************
        * Public                 findDevices
        *
        * Bluetooth function to find all nearby bluetooth devices and return their device information
        *
        * Inputs
        * None
        *                                
        * Outputs
        * BluetoothDeviceInfo[] devices.  An array containing all nearby bluetooth device info.
        * 
        * History:
        ****************************************************************/
        public BluetoothDeviceInfo[] findDevices()
        {
            //Finds and returns all nearby bluetooth devices
            bc = new BluetoothClient();
            devices = bc.DiscoverDevices(10);

            //devices = bc.DiscoverDevicesInRange();
            return devices;
        }

        /***************************************************************
        * Public                 selectDevice
        *
        * Bluetooth function to pair the software to a certain bluetooth device.  Once a connection is established, 
        * BluetoothConnection will spawn a non-ending thread whose main job is to listen and feed incoming data to the queue.
        * 
        * Inputs
        * string deviceName: a string with the desired deviceName stored inside.
        *                                
        * Outputs
        * bool connected: a boolean indicating whether or not a successful connection was established to a bluetooth device.
        * 
        * History:
        ****************************************************************/
        public bool selectDevice(string deviceName)
        {
            //Finds and returns all nearby bluetooth devices
            bc = new BluetoothClient();
            devices = bc.DiscoverDevices(8);

            //Create new BluetoothDevice
            BluetoothDeviceInfo device = null;

            //Go through all nearby bluetooth devices in the area.
            foreach (BluetoothDeviceInfo i in devices)
            {
                //if we find a corresponding device name
                if (i.DeviceName == deviceName)
                {
                    device = i;
                }
            }
            try
            {
                //Attempt to pair with the bluetooth devvice
                BluetoothEndPoint deviceEndPoint = new BluetoothEndPoint(device.DeviceAddress, BluetoothService.SerialPort);
                bc.Connect(deviceEndPoint);

                //Create streams for us to send messages and read messages from the bluetooth module
                write = new BinaryWriter(bc.GetStream());
                read = new BinaryReader(bc.GetStream(), Encoding.UTF8);

                //Indicate that we have successfully captured
                connected = true;
            }
            catch (System.Net.Sockets.SocketException)
            {
                //We were unable to pair with the bluetooth device.
                MessageBox.Show("Unable to pair with selected device.\n\nPlease select a new device or try again", "ERROR", MessageBoxButtons.OK);
                connected = false;
            }

            //Indicate to the main application whether or not we were successful in connecting.
            return connected;
        }

        /***************************************************************
        * Public                 addRecordingQueue
        * ENGINEERING FUNCTIONALITY WILL NOT MAKE FINAL BUILD
        ****************************************************************/
        public void addRecordingQueue(ref InQueue recordingQueue)
        {
            this.recordingQueue = recordingQueue;
            recording = true;
        }

        /***************************************************************
        * Public                 stopRecording
        * ENGINEERING FUNCTIONALITY WILL NOT MAKE FINAL BUILD
        ****************************************************************/
        public void stopRecording()
        {
            recording = false;
        }

        /***************************************************************
        * Public                 monitorTransmissions
        *
        * Bluetooth function to spark a thread which will simply wait for incoming messages
        * 
        * Inputs
        * ref InQueue transmissionQueue: a Queue to store our transmissions in for processing
        *                                
        * Outputs
        * None
        * 
        * History:
        ****************************************************************/
        public void monitorTransmissions(ref InQueue transmissionQueue)
        {
            this.transmissionQueue = transmissionQueue;

            //Spawn thread to listen to the live data feed
            listen = new Thread(new ThreadStart(collectData));
            listen.Start();
        }
        /***************************************************************
        * Public                 killConnection
        *
        * Bluetooth function to terminate the bluetooth connecction and (if running) the thread thats monitoring transmissions.
        * 
        * Inputs
        * None
        *                                
        * Outputs
        * None
        * 
        * History:
        ****************************************************************/

        public void killConnection()
        {
            //Create a handle that we can use to make the thread wait.
            EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.AutoReset);

            //Set connected to false, which will force listen to end it's thread.
            connected = false;

            //While listen isn't finished, wait 10 ms.
            while (listen.IsAlive)
            {
                handle.WaitOne(10);
            }

            if (bc.Connected)
            {
                //Kill the bluetooth connection between software and the device.
                bc.Close();
            }
        }

        /***************************************************************
        * Public                 isConnected
        *
        * Bluetooth function to access the private attribute connected
        * 
        * Inputs
        * None
        *                                
        * Outputs
        * bool connected: boolean value representing whether or not we are currently connected to a bluetooth device.
        * 
        * History:
        ****************************************************************/
        public bool isConnected()
        {
            return connected;
        }

        /***************************************************************
        * Public                 collectData
        *
        * Bluetooth function which will sit in its own thread.  It only has the functionality to read in information from
        * the bluetooth stream.  Once it reads in a full transmission, it will write the transmission out to the Queue.
        * 
        * Inputs
        * None
        *                                
        * Outputs
        * None
        * 
        * History:
        ****************************************************************/
        private void collectData()
        {
            DateTime start = DateTime.Now;
            int counter = 0;

            //This thread will run until the user clicks "Terminate", which causes the connected to go to false.
            while (connected)
            {
                //Create new instance of Bleutooth transmission (In C#, it is not sufficient to overwrite the attributes
                //of the class.  A new memory bank for an instance of BluetoothTransmission must be created.
                transmissionData = new BluetoothTransmission();

                //The BTM will send "NEW" when a new message is coming across the bytestream.  This lets us know when to
                //start
                waitForNew();

                //Read the timestamp off of the Bluetooth Stream
                transmissionData.timestamp = ReadTimestamp();

                //Read the message ID off of the Bluetooth Stream
                transmissionData.messageID = read.ReadByte();

                //Read 4 sensor values off the Bluetooth Stream (Always sends 4 vals, even for PSI, RPM, AFR, and Voltage
                transmissionData.sensors[0] = readSensor();
                transmissionData.sensors[1] = readSensor();
                transmissionData.sensors[2] = readSensor();
                transmissionData.sensors[3] = readSensor();

                //Write the new transmission out to the queue.
                transmissionQueue.writeToQueue(transmissionData);

                if (recording)
                {
                    recordingQueue.writeToQueue(transmissionData);
                }
                counter++;

                DateTime end2 = DateTime.Now;
                TimeSpan span2 = end2 - start;
            }
        }

        /***************************************************************
        * Public                 readSensor
        *
        * Bluetooth Value which reads 2 1 byte values off of the bluetooth stream, and converts it to a 16 bit unsigned
        * integer.
        * 
        * Inputs
        * None
        *                                
        * Outputs
        * UInt16 sensorValue: Returns a 16 bit unsigned integer reflecting one sensor value.
        * 
        * History:
        ****************************************************************/
        private UInt16 readSensor()
        {
            UInt32 sensor = 0;

            //Read in 8 bytes
            sensor = read.ReadByte();

            //shift bytes to the left 8 places
            sensor = sensor << 8;

            //read in additional 8 bytes and add them to sum
            sensor += read.ReadByte();

            return (UInt16)sensor;
        }

        /***************************************************************
        * Public                 ReadTimestamp
        *
        * Bluetooth Value which reads 4 1 byte values off of the bluetooth stream, and converts it to a 32 bit unsigned
        * integer.
        * 
        * Inputs
        * None
        *                                
        * Outputs
        * UInt16 timestamp: Returns a 32 bit unsigned integer reflecting one timestamp value.
        * 
        * History:
        ****************************************************************/
        private UInt32 ReadTimestamp()
        {
            UInt32 timestamp = 0;

            //read in 1st 8 bytes
            timestamp = read.ReadByte();

            //shift all bytes 8 places to the left
            timestamp = timestamp << 8;

            //read in 8 bytes and add to running total
            timestamp += read.ReadByte();

            //shift all bytes 8 places to the left
            timestamp = timestamp << 8;

            //read in 8 bytes and add to running total
            timestamp += read.ReadByte();

            //shift all bytes 8 places to the left
            timestamp = timestamp << 8;

            //read in 8 bytes and add to running total
            timestamp += read.ReadByte();

            return timestamp;
        }

        /***************************************************************
        * Public                         waitForNew()
        *
        * Bluetooth function which will parse the incoming byte stream and wait for the message "NEW" to come across, which signals
        * a new incoming bluetooth transmission
        * 
        * Inputs
        * None
        *                                
        * Outputs
        * UInt16 timestamp: Returns a 32 bit unsigned integer reflecting one timestamp value.
        * 
        * History:
        ****************************************************************/
        private void waitForNew()
        {
            string newString = "NEW";
            char[] readString = {' ', ' ', ' '};
            bool found = false;

            while (!found)
            {
                readString[2] = (char)read.ReadByte();
                                
                if(string.Compare(newString, new string(readString)) == 0)
                {
                    found = true;
                }
                else
                {
                    readString[0] = readString[1];
                    readString[1] = readString[2];
                }
            }
        }
    }
}
